package agi

import (
    "flag"
    "encoding/json"
    "../logger"
    "time"
    "os"
)

type MainConfig struct {
    TasksUrl        string
    Cycle           int
    TraceCycle      int
    LogDir          string
    LogLevel        int
    ShowComplete    int
    IsTest          bool
    IsConsole       bool
}

type Task struct {
    Name           string
    Url            string
    Cycle          int
    PostUrl        string
}

type Tasks struct {
    Tasks[]        Task
}

const (
    CLEAN_NONE int = iota
    CLEAN_START
    CLEAN_COMPLETE
)

var runtimeDir = GetRuntimeDir()
//var configFile string
var globalConfig = MainConfig{"", 0, 0, "", DEBUG, 0, false, false }

////////////////////////////////////////////////////////////////////////////////////////////
// 前置准备函数
////////////////////////////////////////////////////////////////////////////////////////////

func prepare() {
    // 命令行参数预定义
    flag.StringVar(&globalConfig.LogDir, "log-dir", runtimeDir, "日志输出目录")
    flag.IntVar(&globalConfig.LogLevel, "log-level", DEBUG, "日志输出级别")
    flag.IntVar(&globalConfig.Cycle, "cycle", 3600 * 12, "重新加载任务的周期，单位秒")
    flag.IntVar(&globalConfig.TraceCycle, "trace-cycle", 600, "输出心跳信息的周期，单位秒")
    flag.StringVar(&globalConfig.TasksUrl, "tasks", "", "任务列表")
    flag.IntVar(&globalConfig.ShowComplete, "show-complete", 0, "显示请求完成的信息")
    flag.BoolVar(&globalConfig.IsTest, "test", false, "是否测试命令的参数")
    flag.BoolVar(&globalConfig.IsConsole, "console", true, "是否在命令行输出调试")
    // 解析命令行参数
    if len(os.Args) > 1 && os.Args[1] == "--" {
        // 使用linux的start-stop-daemon来启动进程的时候，--会导致参数的解析出错
        // 比如：start-stop-daemon --start --quiet --pidfile --make-pidfile ${PIDFILE} --exec ${DAEMON} -- ${DAEMON_ARGS}
        flag.CommandLine.Parse(os.Args[2:])
    } else {
        flag.Parse()
    }
    // 现在版本的logger貌似有点问题，日志文件不指定而调用输出方法会抛出一个空指针的错误
    logger.SetConsole(globalConfig.IsConsole)
    logger.SetRollingFile(globalConfig.LogDir, "task_service.log", 10, 5, logger.MB)
    logger.SetLevel(globalConfig.LogLevel)

    if len(globalConfig.TasksUrl) <= 0 {
        // linux系统可能没有中文...
        logger.Error("Please specify a valid tasks url!")
        os.Exit(1)
    }

    if !Exist(globalConfig.LogDir) {
        logger.Error("Log direstory does not exist!")
        os.Exit(1)
    }

    // 增加命令参数的调试
    if globalConfig.IsTest {
        os.Exit(0)
    }

    logger.Log("程序主目录:", runtimeDir)
    logger.Log("日志目录:", globalConfig.LogDir, "日志级别:", globalConfig.LogLevel)

//    if len(configFile) > 0 {
//        loadConfig(configFile)
//    }
}

// 本来想额外读取一个json文件，现在已经完全不需要了
//func loadConfig(path string) (bool) {
//    logger.Log("加载配置文件：", path)
//    raw, err := ReadFileByte(path)
//    if err != nil {
//        logger.Warn("加载配置文件出错", err)
//        return false
//    }
//    var inter interface{}
//    json.Unmarshal(raw, &inter)
//    config, ok := inter.(map[string]interface{})
//    if ok {
//        for k, v := range config {
//            js := JsonValue{v }
//            if k == "TasksUrl" {
//                url := js.AsString()
//                if len(url) > 0 {
//                    globalConfig.TasksUrl = url
//                }
//            }
//            if k == "Cycle" {
//                cycle := js.AsNumber()
//                if cycle > 0 {
//                    globalConfig.Cycle = cycle
//                }
//            }
//        }
//        logger.Log("解析配置成功:", globalConfig)
//    } else {
//        logger.Warn("解析配置失败", ok)
//    }
//    return true
//}

////////////////////////////////////////////////////////////////////////////////////////////
// 启动主函数
////////////////////////////////////////////////////////////////////////////////////////////

// 主计时器
var mainTicker *time.Ticker
// 主心跳计数器
var mainCounter = 0
// 任务的计时器
var taskTickers = make(map[string]*time.Ticker)
// 全部的任务清单
var taskList Tasks
// 清理计时器的步骤，默认无
var clearTaskTickersStep = CLEAN_NONE

func TaskServiceStart() {
    prepare()

    ch := make(chan int)
    go startMainTicker()
    go loadTasksList()
    <-ch
}

// 启动主计时器
func startMainTicker() {
    logger.Log("主计时器启动")
    // 如果主计时器不为空，先停止掉主计时器
    stopMainMainTicker()
    // 启动一个全局的计时器
    mainTicker = time.NewTicker(time.Duration(1) * time.Second)
    for {
        select {
        case <- mainTicker.C:
            mainCounter += 1
            // 10分钟输出一次心跳，表明自己没死
            if mainCounter % globalConfig.TraceCycle == 0 {
                logger.Warn("心跳计时器:", mainCounter, "任务数:", len(taskList.Tasks), "计时器数:", len(taskTickers))
            }
            if clearTaskTickersStep == CLEAN_NONE && mainCounter >= int(globalConfig.Cycle) {
                // 开始清理计时器
                clearTaskTickersStep = CLEAN_START
                clearTickers()
                // 这里的操作是阻塞的
            }
            // 已经完成清空计时器了
            if clearTaskTickersStep == CLEAN_COMPLETE {
                clearTaskTickersStep = CLEAN_NONE
                logger.Debug("重启所有任务", "清理进程步骤:", clearTaskTickersStep)
                ch := make(chan int)
                go startMainTicker()
                go loadTasksList()
                <-ch
            }
        }
    }
}

// 增加一个停止主计时器的方法
func stopMainMainTicker() bool {
    if mainTicker != nil {
        mainTicker.Stop()
        return true
    }
    return false
}

// 清理全部的计时器
func clearTickers() {
    l := len(taskTickers)
    logger.Log("需要清理的计时器数量", l)
    if clearTaskTickersStep == CLEAN_START && l > 0 {
        for key, ticker := range taskTickers {
            logger.Debug("清空" + key + "任务计时器")
            ticker.Stop()
            delete(taskTickers, key)
        }
    }
    mainCounter = 0
    clearTaskTickersStep = CLEAN_COMPLETE
}

func loadTasksList() {
    start := time.Now()
    resp, _ := HttpGet(globalConfig.TasksUrl)
    err := json.Unmarshal([]byte(resp), &taskList)
    complete := time.Now()
    if err != nil {
        // 解析任务清单失败，就直接退出主进程。
        logger.Error("解析任务清单失败", complete.Sub(start), err)
        stopMainMainTicker()
        os.Exit(1)
    } else {
        logger.Log("解析任务清单成功", complete.Sub(start))
        t := taskList.Tasks
        l := len(taskList.Tasks)
        var names = make([]string, l)
        for i := 0; i < len(t); i++ {
            names[i] = taskList.Tasks[i].Name
        }
        logger.Log("共有", l, "个任务", names)
        // 开始执行任务
        taskList.start()
    }
}

func (self *Tasks) start() {
    mainCounter = 0
    logger.Log("全任务启动")
    count := len(self.Tasks)
    ch := make(chan int)
    for i := 0; i < count; i++ {
        go self.Tasks[i].start()
    }
    <-ch
}

func (task *Task) start() {
    ch := make(chan int)
    go task.request()
    go task.startTicker()
    <-ch
}

func (task *Task) request() {
    logger.Log(task.Name, "开始")
    start := time.Now()
    resp, _ := HttpGet(task.Url)
    complete := time.Now()
    if globalConfig.ShowComplete > 0 {
        logger.Log(task.Name, "完成:", complete.Sub(start), "响应内容长度:", len(resp))
    }
    if len(task.PostUrl) > 0 {
        HttpPost(task.PostUrl, resp)
    }
}

func (task *Task) startTicker() {
    if taskTickers[task.Name] != nil {
        taskTickers[task.Name].Stop()
        delete(taskTickers, task.Name)
    }
    ticker := time.NewTicker(time.Duration(task.Cycle) * time.Second)
    taskTickers[task.Name] = ticker
    for {
        select {
        case <- ticker.C:
            // 这里如果不用go，则是堵塞的方式，他会堵塞计时器的触发
            // task.request()
            // 下面这个是非堵塞的模式，完全严格按照任务清单给的时间周期去执行
            ch := make(chan int)
            go task.request()
            go task.startTicker()
            <-ch
        }
    }
}
